package com.ruoyi.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 获取application的类
 */
@Order(99)
@Component
public class ApplicationContextUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext ;
    private static DefaultListableBeanFactory defaultListableBeanFactory;
    private static final Logger logger = LoggerFactory.getLogger(ApplicationContextUtil.class);

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
    //通过name获取 Bean.

    public static Object getBean(String name) {

        return getApplicationContext().getBean(name);

    }

    //通过class获取Bean.
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    //通过name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextUtil.applicationContext = applicationContext;
        //将applicationContext转换为ConfigurableApplicationContext
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
        // 获取bean工厂并转换为DefaultListableBeanFactory
        defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
        logger.info("init ApplicationContext and  BeanFactory Success.");
    }

    public static String getProfiles() {
        return getApplicationContext().getEnvironment().getActiveProfiles()[0];
    }

    /**
     * 注册bean到Spring容器。使用构造函数参数初始化bean。
     * 备注：需要有默认构造器，即需要有无参构造器。
     * @param beanName
     * @param clazz
     * @param constructorArgs
     */
    public static void registerBean(String beanName, Class<?> clazz, Object... constructorArgs) {
        registerBean(beanName, clazz, beanDefinitionBuilder -> {
            if(constructorArgs!=null&&constructorArgs.length>0){
                for (Object constructorArg : constructorArgs) {
                    beanDefinitionBuilder.addConstructorArgValue(constructorArg);
                }
            }
        });
    }

    /**
     * 注册bean到spring容器中。使用属性参数初始化bean。
     * @param beanName 名称
     * @param clazz    class
     */
    public static void registerBean(String beanName, Class<?> clazz, Map<String, Object> propertyValueMap) {
        registerBean(beanName, clazz, beanDefinitionBuilder -> {
            logger.info("使用属性参数初始化class[{}]",clazz);
            if(propertyValueMap!=null){
                propertyValueMap.forEach((k,v)->{
                    beanDefinitionBuilder.addPropertyValue(k, v);
                });
            }
        });
    }

    private static void registerBean(String beanName, Class<?> clazz, InitBean initBean) {
        // 1. 检查是否存在重名的bean，如果存在打印警告日志，并且返回，
        if (defaultListableBeanFactory.containsBean(beanName)) {
            logger.warn("The Bean  [{}] for  type [{}] is already exists. Please check.", beanName, clazz.getName());
            return;
        }
        // 2. 通过BeanDefinitionBuilder创建bean定义
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);

        //3. 初始化Bean
        if (initBean != null) {
            initBean.init(beanDefinitionBuilder);
        }

        // 4. 注册bean
        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getRawBeanDefinition());
        logger.info("register bean [{}],Class [{}] success.", beanName, clazz);
    }
}

interface InitBean {
    void init(BeanDefinitionBuilder beanDefinitionBuilder);
}